package com.jse;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IP {

    public static String ip="127.0.0.1";//当前IP
    private static String dbpath;

	private static final Pattern IPV4 = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");

    private static final Pattern IPV6 = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");

    private static final Pattern IPV61 = Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");
    
    
    public static boolean isLocal(String name) {
        return ip.equals(name)||name.equals("localhost")||name.contains("::1")||name.startsWith("169.254.")||name.startsWith("fe80:");
    }
    
    public static boolean isIPv4(final String input) {return IPV4.matcher(input).matches();}

    public static boolean isIPv6Std(final String input){return IPV6.matcher(input).matches();}

    public static boolean isIPv6Hex(final String input) {return IPV61.matcher(input).matches();}
    public static boolean isIPv6(final String input) {return isIPv6Std(input)||isIPv6Hex(input);}
    
    public static boolean isIp(String ip){return isIPv4(ip)||isIPv6(ip);}
    
    /**
     * @param inetAddr IP地址
     * @return 检查inetAddr是否为保留IP
     */
    public static boolean isReservedAddr(InetAddress inetAddr) {
        return inetAddr.isAnyLocalAddress()||inetAddr.isLinkLocalAddress()||inetAddr.isLoopbackAddress();
    }
    /**
     * 获取客户机的ip地址
     * @param req
     * @return
     */
    public static String ip(){try{return InetAddress.getLocalHost().getHostAddress();}catch(UnknownHostException e){return "127.0.0.1";}}
    
    
    /**
     * 获取外网ip 如果本地找不到外网ip则返回填入的localip
     * @param localip
     * @return
     */
    public static String internetIP() {
        String ip = "";
        String input=Http.get("https://ip.tool.chinaz.com");
        Pattern p = Pattern.compile("\\<span id\\=\"IpValue\">(.*?)\\<\\/span>");
        Matcher m = p.matcher(input);//\\<em id\\=\"infoLocation\">(.*?)\\<\\/em>
        if (m.find()) {
            String ipstr = m.group(1);
            ip = ipstr;
        }
        return ip;
    }
    
    private static final Pattern PRIVATEIP=Pattern.compile("^(127\\.0\\.0\\.1)|(localhost)|"
    		+ "(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\."
    		+ "\\d{1,3})|(192\\.168\\.\\d{1,3}\\.\\d{1,3})|(\\d|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|"
    		+ "[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$");
    /**
     * 私有ip
     * @param ip
     * @return
     */
    public static boolean isPrivate(String ip) {return PRIVATEIP.matcher(ip).matches();}
     /**
      * 
      * getAvaliIpList:(根据IP白名单设置获取可用的IP列表).
      * 
      * @date 2017-4-17 下午02:50:20
      * @param ipConfig
      * @return
      */

     private static Set<String> getAvaliIpList(String allowIp) {
         Set<String> ipList = new HashSet<String>();
         for (String allow : allowIp.replaceAll("\\s", "").split(";")) {
             if (allow.indexOf("*") > -1) {
                 String[] ips = allow.split("\\.");
                 String[] from = new String[] { "0", "0", "0", "0" };
                 String[] end = new String[] { "255", "255", "255", "255" };
                 List<String> tem = new ArrayList<String>();
                 for (int i = 0; i < ips.length; i++)
                     if (ips[i].indexOf("*") > -1) {
                         tem = complete(ips[i]);
                         from[i] = null;
                         end[i] = null;
                     } else {
                         from[i] = ips[i];
                         end[i] = ips[i];
                     }
                 StringBuffer fromIP = new StringBuffer();
                 StringBuffer endIP = new StringBuffer();
                 for (int i = 0; i < 4; i++)
                     if (from[i] != null) {
                         fromIP.append(from[i]).append(".");
                         endIP.append(end[i]).append(".");
                     } else {
                         fromIP.append("[*].");
                         endIP.append("[*].");
                     }
                 fromIP.deleteCharAt(fromIP.length() - 1);
                 endIP.deleteCharAt(endIP.length() - 1);

                 for (String s : tem) {
                     String ip = fromIP.toString().replace("[*]",
                             s.split(";")[0])
                             + "-"
                             + endIP.toString().replace("[*]", s.split(";")[1]);
                     if (validate(ip)) {
                         ipList.add(ip);
                     }
                 }
             } else {
                 if (validate(allow)) {
                     ipList.add(allow);
                 }
             }
         }
         return ipList;
     }
     private static Pattern pattern = Pattern
             .compile("(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
                     + "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
                     + "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
                     + "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})");
     /**
      * 在添加至白名单时进行格式校验
      * @param ip
      * @return
      */
     private static boolean validate(String ip) {
         for (String s : ip.split("-"))
             if (!pattern.matcher(s).matches()) {
                 return false;
             }
         return true;
     }
     
     /**
      * 对单个IP节点进行范围限定
      * 
      * @param arg
      * @return 返回限定后的IP范围，格式为List[10;19, 100;199]
      */
     private static List<String> complete(String arg) {
         List<String> com = new ArrayList<String>();
         if (arg.length() == 1) {
             com.add("0;255");
         } else if (arg.length() == 2) {
             String s1 = complete(arg, 1);
             if (s1 != null)
                 com.add(s1);
             String s2 = complete(arg, 2);
             if (s2 != null)
                 com.add(s2);
         } else {
             String s1 = complete(arg, 1);
             if (s1 != null)
                 com.add(s1);
         }
         return com;
     }

     private static String complete(String arg, int length) {
         String from = "";
         String end = "";
         if (length == 1) {
             from = arg.replace("*", "0");
             end = arg.replace("*", "9");
         } else {
             from = arg.replace("*", "00");
             end = arg.replace("*", "99");
         }
         if (Integer.valueOf(from) > 255)
             return null;
         if (Integer.valueOf(end) > 255)
             end = "255";
         return from + ";" + end;
     }

     
     
     /**
      * 
      * checkLoginIP:(根据IP,及可用Ip列表来判断ip是否包含在白名单之中).
      * @date 2017-4-17 下午03:01:03
      * @param ip
      * @param ipList
      * @return
      */
     private static boolean checkLoginIP(String ip, Set<String> ipList) {
         if (ipList.isEmpty() || ipList.contains(ip))
             return true;
         else {
             for (String allow : ipList) {
                 if (allow.indexOf("-") > -1) {
                     String[] from = allow.split("-")[0].split("\\.");
                     String[] end = allow.split("-")[1].split("\\.");
                     String[] tag = ip.split("\\.");

                     // 对IP从左到右进行逐段匹配
                     boolean check = true;
                     for (int i = 0; i < 4; i++) {
                         int s = Integer.valueOf(from[i]);
                         int t = Integer.valueOf(tag[i]);
                         int e = Integer.valueOf(end[i]);
                         if (!(s <= t && t <= e)) {
                             check = false;
                             break;
                         }
                     }
                     if (check) {
                         return true;
                     }
                 }
             }
         }
         return false;
     }
     
     /**
      * 
      * checkLoginIP:(根据IP地址，及IP白名单设置规则判断IP是否包含在白名单).
      * @date 2017-4-17 下午03:01:37
      * @param ip
      * @param ipWhiteConfig "1.168.1.1;" //设置单个IP的白名单 // 
    		  + "192.*； " //设置ip通配符,对一个ip段进行匹配 
    		  + "192.168.3.17-192.168.3.38； " //设置一个IP范围
    		  + "25.168.4.0/24 "; //設置一个网段
      * @return
      */
     public static boolean checkIP(String ip,String ipWhiteConfig){
    	 if(Strings.isEmpty(ipWhiteConfig))return false;
    	 if(ipWhiteConfig.startsWith("block:")) {//黑名单
	 		ipWhiteConfig=ipWhiteConfig.substring(6);
	 		Set<String> ipList = getAvaliIpList(ipWhiteConfig);
	        return !checkLoginIP(ip, ipList);
	 	 }else if(ipWhiteConfig.startsWith("white:")) {//白名单 没前缀默认
	 		ipWhiteConfig=ipWhiteConfig.substring(6);
		 }
         Set<String> ipList = getAvaliIpList(ipWhiteConfig);
         return checkLoginIP(ip, ipList);
     }
     
     private static byte[] ipbytes;

     public static String search(String ip) throws Exception {
         long iplong = iplong(ip);
         if(ipbytes==null)ipbytes=loadIPDB(dbpath);
         int sPtr = 0, ePtr = 0;
         int il0 = (int) ((iplong >> 24) & 0xFF);
         int il1 = (int) ((iplong >> 16) & 0xFF);
         int idx = il0 * 256 * 8 + il1 * 8;
         sPtr = getInt(ipbytes, 256 + idx);
         ePtr = getInt(ipbytes, 256 + idx + 4);
         final byte[] buff = new byte[14];
         int dataLen = -1, dataPtr = -1;
         int l = 0, h = (ePtr - sPtr) / 14;
         while (l <= h) {
             int m = (l + h) >> 1;
             int p = sPtr + m * 14;
             read(p, buff);
             long sip = getIntLong(buff, 0);
             if (iplong < sip) {
                 h = m - 1;
             } else {
                 long eip = getIntLong(buff, 4);
                 if (iplong > eip) {
                     l = m + 1;
                 } else {
                	 int offset=8;
                     dataLen =((buff[offset++]&0x000000FF)|(buff[offset]<<8)&0x0000FF00);
                     dataPtr = getInt(buff, 10);
                     break;
                 }
             }
         }
         if (dataPtr < 0) {return null;}
         final byte[] regionBuff = new byte[dataLen];
         read(dataPtr, regionBuff);
         return new String(regionBuff, "utf-8");
     }
     
     private static void read(int offset, byte[] buffer) throws IOException {
         System.arraycopy(ipbytes, offset, buffer, 0, buffer.length);
     }

     private static byte[] loadIPDB(String dbPath) throws IOException {
    	 final var handle = Files.newByteChannel(Path.of(dbPath));
    	 handle.position(0);
         final var buff= ByteBuffer.allocate((int)handle.size());
         int rLen = handle.read(buff);
         if (rLen != buff.capacity()) {
             throw new IOException("incomplete read: read bytes should be " + buff.capacity());
         }
         byte[] arr=buff.array();
         handle.close();
         return arr;
     }
     
     private static long getIntLong(byte[] b, int offset) {
         return (
             ((b[offset++] & 0x000000FFL)) |
             ((b[offset++] <<  8) & 0x0000FF00L) |
             ((b[offset++] << 16) & 0x00FF0000L) |
             ((b[offset  ] << 24) & 0xFF000000L)
         );
     }
     private static int getInt(byte[] b, int offset) {
         return (
             ((b[offset++] & 0x000000FF)) |
             ((b[offset++] <<  8) & 0x0000FF00) |
             ((b[offset++] << 16) & 0x00FF0000) |
             ((b[offset  ] << 24) & 0xFF000000)
         );
     }

     public static String long2ip(long ip)
     {
         return String.valueOf((ip>>24)&0xFF)+'.'+((ip>>16)&0xFF)+'.'+((ip>>8)&0xFF)+'.'+((ip)&0xFF);
     }

     private static final byte[] shiftIndex = {24, 16, 8, 0};
     public static long iplong(String ip) throws Exception {
         String[] ps = ip.split("\\.");
         if (ps.length != 4) {
             throw new Exception("invalid ip address `" + ip + "`");
         }
         long ipDst = 0;
         for (int i = 0; i < ps.length; i++) {
             int val = Integer.parseInt(ps[i]);
             if (val > 255) {
                 throw new Exception("ip part `"+ps[i]+"` should be less then 256");
             }
             ipDst |= ((long) val << shiftIndex[i]);
         }
         return ipDst & 0xFFFFFFFFL;
     }

	
	public static void main(String[] args) throws Exception {
		boolean is=checkIP("192.168.0.2","block:192.168.*");
//		boolean is=isPrivate("172.18.22.33");
//		var is=search("42.227.165.106");
		System.err.println(internetIP());
	}
}
